from owlready2 import *

onto = get_ontology('http://test.org/onto.owl')
with onto:
    class Drug(Thing):
        pass
    class Ingredient(Thing):
        pass
    # class has_for_ingredient(ObjectProperty):
    #     domain = [Drug]
    #     range = [Ingredient]
    # 另一种写法
    class has_for_ingredient(Drug >> Ingredient):
        pass


def get_domain_range():
    print('1. get_domain_range')
    print('\t', has_for_ingredient.domain)
    print('\t', has_for_ingredient.range)

def create_relation():
    print('2. create_relation')
    with onto:
        my_drug = Drug('my_drug')
        acetaminophen = Ingredient('acetaminophen') # 对乙酰氨基苯酚
        my_drug.has_for_ingredient = [acetaminophen]
        print('\t', my_drug.has_for_ingredient)
        codine = Ingredient('codeine') # 可待因
        my_drug.has_for_ingredient.append(codine)
        print('\t', my_drug.has_for_ingredient)

# 数据属性支持类型:
# int
# float
# bool
# str(string)
# owlready2.normstr 单行字符串
# owlready2.locstr 本地化字符串, 带有语言关联
# datetime.date
# datetime.time
# datetime.datetime
def data_property():
    print('3. data_property')
    with onto:
        # 同义词
        class has_for_synonym(Thing >> str):
            pass
        onto.acetaminophen.has_for_synonym = ['acetaminophen', 'paracetamol']
        print('\t', onto.acetaminophen.has_for_synonym)
        
def inverse_properties():
    print('4. inverse_properties')
    with onto:
        class is_ingredient_of(Ingredient >> Drug):
            inverse_property = has_for_ingredient
        # owl2会自动发现反向属性并自动更新
        my_drug2 = Drug('my_drug2')
        aspirin = Ingredient('aspirin')
        my_aspirin = Ingredient('my_aspirin')
        my_drug2.has_for_ingredient.append(aspirin)
        my_drug2.has_for_ingredient.append(my_aspirin)
        print('\tmy_drug2.has_for_ingredient =', my_drug2.has_for_ingredient)
        print('\taspirin_is_ingredient_of =', aspirin.is_ingredient_of)
        aspirin.is_ingredient_of = []
        print('\taspirin.is_ingredient_of = []...')
        print('\tmy_drug2.has_for_ingredient =', my_drug2.has_for_ingredient)
        print('\taspirin_is_ingredient_of =', aspirin.is_ingredient_of)

def functional_inverse_properties():
    print('5. functional_inverse_properties')
    with onto:
        class has_for_cost(Drug >> float, FunctionalProperty):
            pass
        print('5.1 set funcation DataProperty')
        print('\tonto.my_drug.has_for_cost =', onto.my_drug.has_for_cost)
        print('\t set has_for_cost...')
        onto.my_drug.has_for_cost = 4.2
        print('\tonto.my_drug.has_for_cost =', onto.my_drug.has_for_cost)
        print('5.2 guess for functional')
        class prop(ObjectProperty): pass
        class A(Thing): pass
        class B(Thing):
            is_a = [ prop.max(1) ]
        print('\tA().prop =', A().prop)
        print('\tB().prop =', B().prop) # 没有像文档中手的一样B().prop = None
        print('5.3 inverse funcational') # 双向一对一?
        class Man(Thing):pass
        class Woman(Thing):pass
        class hasHusband(Woman >> Man, FunctionalProperty, InverseFunctionalProperty): pass
        man1 = Man('man1')
        man2 = Man('man2')
        woman1 = Woman('woman1')
        woman2 = Woman('woman2')
        woman3 = Woman('woman3')
        woman1.hasHusband = man1
        woman2.hasHusband = man1 # 违反InverseFuncationalProperty
        # woman3.hasHusband = [man1, man2] # 违反FuncationalProperty
        # sync_reasoner()
        print('\twoman1.hasHusband', woman1.hasHusband)
        print('\twoman2.hasHusband', woman2.hasHusband)
        # 这两处违反在开放世界中会被推理出woman1和woman2是同一个人, man1和man2也是同一个人
        # 如果设定woman1和women2不是同一个人, man1和man2不是同一个人, 推理就会出现矛盾

def create_subproperty():
    print('6. create_subproperty')
    with onto:
        class ActivePrinciple(Ingredient):
            pass
        class has_for_active_principle(Drug >> ActivePrinciple, has_for_ingredient):
            pass 

def obtaining_indirect_relations():
    print('7. obtaining_indirect_relations')
    # 属性名可以'INDIRECT_'作为前缀来获得所有非直接的关系实体, 关系如下
    # transitive, symmetric and reflexive properties,
    # property inheritance (i.e. subproperties),
    # classes of an individual (i.e. values asserted at the class level),
    # class inheritance (i.e. parent classes).
    # equivalences (i.e. equivalent classes, identical “same-as” individuals,…)
    with onto:
        class BodyPart(Thing): pass
        class part_of(BodyPart >> BodyPart, TransitiveProperty): pass
        abdomen = BodyPart('abdomen')#腹部
        heart = BodyPart('heart', part_of = [abdomen])#心脏
        left_ventricular = BodyPart('left_ventricular', part_of=[heart])# 左心室
        kidney = BodyPart('kidney', part_of=[abdomen])#肾
        print('\tleft_ventricular.part_of: ', left_ventricular.part_of)
        print('\tleft_ventricular.INDIRECT_part_of: ', left_ventricular.INDIRECT_part_of)

def associating_python_properties():
    print('8. associating_python_properties')
    with onto:
        my_drug3 = Drug('my_drug3')
        cetirizin = Ingredient('cetirizin')
        # my_drug3.has_for_ingredient.append(cetirizin)
        # print('\tmy_drug3.has_for_ingredient: ', my_drug3.has_for_ingredient)
        # ???BUG: 经过这句设定最后一行会抛出异常, 如果注释掉这一行最后一行可执行
        # has_for_ingredient.python_name = 'ingredients'
        my_drug3.ingredients = [cetirizin]
        print('\tmy_drug3.ingredients =', my_drug3.ingredients)
        print('\tmy_drug3.has_for_ingredient', my_drug3.has_for_ingredient)

def get_relation_instances():
    print('9. get_relation_instances')
    for relation in onto.has_for_active_principle.get_relations():
        print('\t', relation)

get_domain_range()
create_relation()
data_property()
inverse_properties()
functional_inverse_properties()
create_subproperty()
obtaining_indirect_relations()
associating_python_properties()
get_relation_instances()